import { isFunction, Before } from './Aop';

/**
 * 绑定类方法的this为当前类实例
 * 
 * @export
 * @param {Array<string>} [methods=null] 指定要绑定的方法，不指定则绑定全部
 * @param {boolean} [include = true] include为true则methods数组为将要执行绑定的方法，为false则methods为将要排除的方法
 * @param {number} [inherits=0] 绑定继承的方法层级，默认为0不绑定继承的方法，为负则遍历所有原型链
 * @returns
 */
export function BindMethodThis(methods: Array<string> = null, include = true, inherits: number = 0) {
    return function (_constructor: Function) {
        return Before(function (self, target, ...args) {
            getAllNeedBindMethodNames(_constructor).forEach(name => {
                self[name] = self[name].bind(self);
            });
        })(_constructor);
    };

    // 获取需要绑定this的方法
    function getAllNeedBindMethodNames(_constructor: Function) {
        let prototype = _constructor.prototype,
            methodNames: Array<string> = getObjectFunctionNames(prototype),
            proto: any = null,
            _inherits = inherits;
        // 指定了绑定继承的方法则需要遍历获取原型链
        while (_inherits-- && (proto = Object.getPrototypeOf(prototype))) {
            let names = getObjectFunctionNames(proto);
            names.forEach(name => {
                if (methodNames.indexOf(name) === -1) {
                    methodNames.push(name);
                }
            });
        }
        if (methods && include) {
            // 过滤规则:包含
            methodNames = methods.filter(name => methodNames.indexOf(name) !== -1);
        } else if (methods && !include) {
            // 过滤规则:排除
            methodNames = methodNames.filter(name => methodNames.indexOf(name) !== -1 && methods.indexOf(name) === -1);
        }
        return methodNames;
    }
}

/**
 * 获取一个对象上的所有函数
 * 
 * @param {Object} obj
 * @returns {Array<string>}
 */
function getObjectFunctionNames(obj: any): Array<string> {
    let names = Object.getOwnPropertyNames(obj);
    return names.filter((name: string) => isFunction(obj[name]));
}
